/**
 * The Chart class
 * @author  Lorenzo Baracchi <lorenzo.baracchi@usi.ch> 
 */

/**
 * create a Chart - canvas is the canvas where the chart will be displayed -
 * numX is the number of unit for x-axis - numY is the number of unit for y-axis -
 * stroke is the color used to draw lines
 */
function SimpleChart(canvas, numX, numY, stroke) {
	this.data = new Array(); // a list of data to plot
	this.showSeries = new Array(); // array strong which series to show
	this.seriesPerUnitX = 0; // number of series per unit of x-axis
	this.numX = numX;
	this.numY = numY + 12; // +12 is a little padding to display the tooltip
	this.canvas = canvas;
	this.context = this.canvas.getContext("2d");
	this.stroke = stroke;
	this.axisYPadding = 50;
	this.axisXPadding = 25;
	this.padding = 5;
	this.labels = null

	this.drawParameters = null
	this.currentRange=[]

	this.addData = function(data) {
		this.data.push(data);
		this.showSeries.push(data.id);
		if (data.type == "bar")
			this.seriesPerUnitX += 1;
	}

	this.getHeight = function() {
		return this.canvas.height - this.axisYPadding;
	}

	this.getWidth = function() {
		return this.canvas.width - (2 * this.axisXPadding);
	}

	this.setLabels = function(listlabes) {
		this.labels = listlabes;
	}

	/**
	 * Convert the y position from standard Cartesian coordinates to something
	 * that can be used in canvas
	 */
	this.getChartY = function(y) {
		return -(this.getHeight() / this.numY * y);
	}

	/**
	 * Get the maximum width of a series
	 */
	this.getChartXWidth = function(seriesPerUnitX) {
		return this.getWidth() / this.numX / seriesPerUnitX - this.padding;
	}

	/**
	 * Convert the x position from standard Cartesian coordinates to something
	 * that can be used in canvas
	 */
	this.getChartX = function(x, seriesPerUnitX, seriesIndex) {
		var tmp = this.getWidth() / this.numX * x + seriesIndex
				* this.getChartXWidth(seriesPerUnitX) + this.axisXPadding/2;
		return tmp;
	}

	/**
	 * Set the list of series to show by a list of series ids
	 */
	this.setShowSeries = function(listID) {
		this.showSeries = listID;
		this.seriesPerUnitX = this.showSeries.length;
	}

	this.setSeriesPerUnitX = function(number) {
		this.seriesPerUnitX = number;
	}
	
	this.getCurrentRange = function() {
		return this.currentRange;
	} 

	/**
	 * Draws the chart for the given range, the parameter redraw is optional
	 */
	this.drawChart = function(range, redraw) {
		
		this.currentRange = range;

		redraw = typeof redraw !== 'undefined' ? redraw : false;
		this.drawParameters = range;
		var tmp = this.numX;
		this.numX = range[1] - range[0];

		var tooltip = null;

		this.drawAxis(range);
		for ( var i in this.data) {
			var ind = this.showSeries.indexOf(this.data[i].id);
			if (ind != -1) {
				switch (this.data[i].type) {
				case "bar":
					var t = this.drawBar(ind, this.data[i], range, redraw)
					if (t[2] != "")
						tooltip = t;
					break;
				case "point":
					break;
				case "line":
					break;
				}
			}
		}
		if (tooltip != null) {
			this.context.fillStyle = this.stroke;
			this.context.fillText(tooltip[2], tooltip[0], tooltip[1]);
		}

		this.numX = tmp;
	}

	/**
	 * Draw the bar chart for the series at given index the series, the
	 * parameter redraw is optional
	 */
	this.drawBar = function(index, series, range, redraw) {

		redraw = typeof redraw !== 'undefined' ? redraw : false;
		if (!redraw) {
			for ( var i in this.data)
				series.shapes = new Array(series.x.length)
		}

		this.context.strokeStyle = this.stroke;
		this.context.fillStyle = series.color;

		var tooltipX = -1;
		var tooltipY = -1;
		var tooltipText = "";
		// for ( var j = range[0]; j < range[1] && j < series.y.length; j++) {
		for ( var j = 0; j < series.y.length; j++) {
			if (series.x[j] < range[0] || series.x[j] > range[1])
				continue; // the data is not in the rage to draw

			var xToDraw = this.getChartX(series.x[j] - range[0],
					this.seriesPerUnitX, index);
			var yToDraw = this.canvas.height - this.axisYPadding;
			var widthToDraw = this.getChartXWidth(this.seriesPerUnitX);
			var heightToDraw = this.getChartY(series.y[j]);

			if (series.shapes[j] != null) {
				if (series.shapes[j].selected)
					this.context.fillStyle = "rgb(77,61,125)";
				else if (series.shapes[j].over) {
					tooltipText = series.id + ": " + series.y[j];
					tooltipX = xToDraw - tooltipText.length;
					tooltipY = yToDraw + heightToDraw - 1;
					this.context.fillStyle = "rgb(124,98,201)";
				} else
					this.context.fillStyle = series.getColor()+"";
			} else
				this.context.fillStyle = series.getColor()+"";
			this.context.fillRect(xToDraw, yToDraw, widthToDraw, heightToDraw);

			if (series.shapes[j] == null || !redraw) {
				var shape = new Shape(xToDraw, yToDraw, widthToDraw,
						heightToDraw, series.getColor()+"");
				series.setShape(j, shape);
			}
		}
		return [ tooltipX, tooltipY, tooltipText ];
	}

	/**
	 * Draw the axis
	 */
	this.drawAxis = function(range) {
		// x axis
		for ( var a = range[0]; a < range[1]; a++) {
			var x = this.getChartX(a - range[0], this.seriesPerUnitX, 0);
			var y = this.canvas.height - this.axisYPadding;
			var w = this.getChartXWidth(this.seriesPerUnitX);

			// label
			this.context.fillStyle = this.stroke;
			if (this.labels == null)
				this.context.fillText(a + "", x + w / 3,
						this.canvas.height - 10);
			else {
				this.context.save();
				this.context.translate(x + w / 3, y);
				this.context.rotate(Math.PI / 2);
				if (this.labels[a] != null)
					this.context.fillText(" " + this.labels[a], 0, 0);
				else
					this.context.fillText("", 0, 0 + 20);
				this.context.restore();
			}
		}

		// grid for y axis
		this.context.strokeStyle = this.strokegrid;
		var step = this.numY / 10;
		for ( var j = 0; j < this.numY; j += step) {
			var y = this.canvas.height - this.axisYPadding + this.getChartY(j);

			// draw line
			if (j == 0) {
				this.context.strokeStyle = this.stroke;
				this.context.beginPath();
				this.context.moveTo(this.axisXPadding/2, y);
				this.context.lineTo(this.canvas.width - this.axisXPadding, y);
				this.context.closePath();
				this.context.stroke();
			}

		}
	}

	this.clear = function() {
		this.context.clearRect(0, 0, canvas.width, canvas.height);
	}

	this.redraw = function() {
		this.clear();
		this.drawChart(this.drawParameters, true);
	}

	/* Event listeners for mouse movements */

	this.selectedShape = -1;
	this.selectedData = -1;
	this.overShape = -1;
	this.overData = -1;
	var myState = this; // need to have a reference to the chart in the
						// listeners

	/**
	 * Click of the mouse
	 */
	canvas
			.addEventListener(
					'mousedown',
					function(e) {
						var selected = false;
						var mouse = myState.getMouse(e);
						var mx = mouse.x;
						var my = mouse.y;
						for ( var k in myState.data) {
							var shapes = myState.data[k].shapes;
							var l = shapes.length;
							for ( var i = 0; i < l; i++) {
								if (shapes[i] != null
										&& shapes[i].contains(mx, my)) {
									if (myState.selectedData != -1
											&& myState.selectedShape != -1
											&& myState.data[myState.selectedData].shapes[myState.selectedShape] != null)
										myState.data[myState.selectedData].shapes[myState.selectedShape].selected = false;
									myState.selectedShape = i;
									myState.selectedData = k;
									shapes[i].selected = true;
									selected = true;
									break;
								}
								if (selected)
									break;
							}
						}
						if (!selected) {
							if (myState.selectedShape != null
									&& shapes[myState.selectedShape] != null) {
								myState.data[myState.selectedData].shapes[myState.selectedShape].selected = false;
								// this.selectedShape = null;
							}
						}
						myState.redraw();

					}, true);

	/**
	 * Mouse movement
	 */
	canvas
			.addEventListener(
					'mousemove',
					function(e) {
						var over = false;
						var mouse = myState.getMouse(e);
						var mx = mouse.x;
						var my = mouse.y;
						for ( var k in myState.data) {
							var shapes = myState.data[k].shapes;
							var l = shapes.length;
							for ( var i = 0; i < l; i++) {
								if (shapes[i] != null
										&& shapes[i].contains(mx, my)) {
									if (myState.overData != -1
											&& myState.overShape != -1
											&& myState.data[myState.overData].shapes[myState.overShape] != null)
										myState.data[myState.overData].shapes[myState.overShape].over = false;
									myState.overShape = i;
									myState.overData = k;
									shapes[i].over = true;
									over = true;
									break;
								}
							}
							if (over)
								break;
						}
						if (!over) {
							if (myState.hoverShape != -1
									&& shapes[myState.overShape] != null) {
								myState.data[myState.overData].shapes[myState.selectedShape].over = false;
								// this.overShape = null;
							}
						}

						myState.redraw();
					}, true);

	/**
	 * Get the position of the mouse
	 */
	this.getMouse = function(e) {
		var targ;
		if (!e)
			e = window.event;
		if (e.target)
			targ = e.target;
		else if (e.srcElement)
			targ = e.srcElement;
		if (targ.nodeType == 3) // defeat Safari bug
			targ = targ.parentNode;

		// jQuery normalizes the pageX and pageY
		// pageX,Y are the mouse positions relative to the document
		// offset() returns the position of the element relative to the document
		var mx = e.pageX - $(targ).offset().left;
		var my = e.pageY - $(targ).offset().top;

		// We return a simple javascript object (a hash) with x and y defined
		return {
			x : mx,
			y : my
		};
	}

}
